Framework.IO = {}

local ActiveSession = {}

local function FormatDebugString( Str, Info )
	local Info = Info or { currentline = 0, source = "Unknown" }
	if(Str:find("{line}"))then Str = Str:gsub( "{line}", Info.currentline ) end
	if(Str:find("{file}"))then Str = Str:gsub( "{file}", Info.source:match( "/(%a+.lua)" ) ) end
	if(Str:find("{path}"))then Str = Str:gsub( "{path}", util.RelativePathToFull( Info.source ) ) end
	if(Str:find("{time}"))then Str = Str:gsub( "{time}", os.date( "%X" ) ) end
	if(Str:find("{date}"))then Str = Str:gsub( "{date}", os.date( "%x" ) ) end
	if(Str:find("{side}"))then Str = Str:gsub( "{side}", (SERVER && "Server" || "Client" ) ) end
	return Str
end

function Framework.IO.Print( ST_String, OP_NU_DebugLevel )
	local Info = debug.getinfo( OP_NU_DebugLevel or 2, "nSl" )
	print( FormatDebugString( ST_String, Info ) )
end

function Framework.IO.Debug( ST_String, OP_NU_DebugLevel )
	if( Framework.Data.Get( "Framework.IO.PrintDebug" ) ) then
		Framework.IO.Print( "[{path}:{line}] Debug: " .. ST_String, (OP_NU_DebugLevel or 2)+1 )
	end
	if( Framework.Data.Get( "Framework.IO.LogDebug" ) ) then
		Framework.IO.Log( "Debug [{path}:{line}] " .. ST_String, (OP_NU_DebugLevel or 2)+1 )
	end
end

function Framework.IO.Warning( ST_String, OP_NU_DebugLevel )
	if( Framework.Data.Get( "Framework.IO.PrintWarning" ) ) then
		Framework.IO.Print( "[{path}:{line}] Warning: " .. ST_String, (OP_NU_DebugLevel or 2)+1 )
	end
	if( Framework.Data.Get( "Framework.IO.LogWarning" ) ) then
		Framework.IO.Log( "Warning [{path}:{line}] " .. ST_String, (OP_NU_DebugLevel or 2)+1 )
	end
end

function Framework.IO.Error( ST_String, OP_NU_DebugLevel )
	if( Framework.Data.Get( "Framework.IO.PrintError" ) ) then
		Framework.IO.Print( "[{path}:{line}] Error: " .. ST_String, (OP_NU_DebugLevel or 2)+1 )
	end
	if( Framework.Data.Get( "Framework.IO.LogError" ) ) then
		Framework.IO.Log( "Error [{path}:{line}] " .. ST_String, (OP_NU_DebugLevel or 2)+1 )
	end
end

local ErrorBuffer = {}
function Framework.IO.Log( ST_String, OP_NU_DebugLevel )
	local Info = debug.getinfo( OP_NU_DebugLevel, "nSl" )
	if( !ActiveSession.Filename ) then
		table.insert( ErrorBuffer, FormatDebugString( "\n[{side}|{date} {time}] " .. ST_String, Info ) )
		return
	end
	filex.Append( ActiveSession.Filename, FormatDebugString( "\n[{side}|{date} {time}] " .. ST_String, Info ) )
end

timer.Simple( .1, function()
	if( Framework.Data.Exists( "Framework.IO.SessionCounter" ) ) then
		ActiveSession.Session = Framework.Data.Get( "Framework.IO.SessionCounter" )
		Framework.Data.Increment( "Framework.IO.SessionCounter" )
	else
		ActiveSession.Session = 1
		Framework.Data.New( "Framework.IO.SessionCounter", 1, DATA_PRIORITY )
	end
	ActiveSession.Timestamp = os.date( "%m-%d-%y %H-%M-%S" )
	ActiveSession.Filename  = "Framework/IO/Logs/" .. (CLIENT && "Client" || "Server") .. "/#" .. ActiveSession.Session .. " - " .. os.date( "%m-%d-%y %H-%M-%S" ) .. ".txt"
	
	local s = "--- " .. (CLIENT && "Client" || "Server") .. " Session #" .. ActiveSession.Session .. " - " .. ActiveSession.Timestamp .. " ---"
	file.Write( ActiveSession.Filename, string.rep( "-", s:len() ) .. "\n" .. s .. "\n" .. string.rep( "-", s:len() ) .. "\n" )
	
	for k, v in pairs( ErrorBuffer )do
		filex.Append( ActiveSession.Filename, v )
	end
end )

local ChatCommands = {}

function Framework.IO.AddChatCommand( ST_Name, FU_Function, OP_ST_Delimiter, OP_FU_AutoComplete )
	ChatCommands[ST_Name] = { Func = FU_Function, Delimiter = OP_ST_Delimiter, AutoComplete = OP_FU_AutoComplete }
end

function Framework.IO.RemoveChatCommand( ST_Name )
	ChatCommands[ST_Name] = nil
end

if( SERVER ) then
	hook.Add( "PlayerSay", "Framework.IO.ChatCommandChecker", function( PL_Player, ST_Text )
		for k, v in pairs( ChatCommands ) do
			if( ST_Text:find( "^" .. k .. "[%s$]-" ) ) then
				local Args = ST_Text:match( "^.-[%s$]+(.*)" )
				local RetVal
				if( v.Delimiter ) then
					RetVal = v.Func( PL_Player, k, unpack(string.Explode( v.Delimiter, Args ) ) )
				else
					RetVal = v.Func( PL_Player, k, Args )
				end
				if( RetVal != nil ) then
					return RetVal
				end
				break
			end
		end
	end )
else
	hook.Add( "ChatTextChanged", "Framework.IO.ChatAutoComplete", function( ST_Text )
		for k, v in pairs( ChatCommands ) do
			if( ST_Text:find( "^" .. k .. "[%s$]-" ) ) then
				local Args = ST_Text:match( "^.-[%s$]+(.*)" )
				if( v.AutoComplete ) then
					if( v.Delimiter ) then
						v.AutoComplete( k, unpack(string.Explode( v.Delimiter, Args )) )
					else
						v.AutoComplete( k, Args )
					end
				end
				break
			end
		end
	end )
	hook.Add( "OnPlayerChat", "Framework.IO.ChatCommandChecker", function( PL_Player, ST_Text )
		for k, v in pairs( ChatCommands ) do
			if( ST_Text:find( "^" .. k .. "[%s$]-" ) ) then
				local Args = ST_Text:match( "^.-[%s$]+(.*)" )
				local RetVal
				if( v.Delimiter ) then
					RetVal = v.Func( PL_Player, k, unpack(string.Explode( v.Delimiter, Args ) ) )
				else
					RetVal = v.Func( PL_Player, k, Args )
				end
				if( RetVal != nil ) then
					return RetVal
				end
				break
			end
		end
	end )
end

timer.Simple( .2, function()
	local DefaultValues = {
		PrintDebug = true,
		LogDebug = true,
		PrintWarning = true,
		LogWarning = true,
		PrintError = true,
		LogError = true
	}
	
	for k, v in pairs( DefaultValues ) do
		local n = "Framework.IO." .. k
		if( !Framework.Data.Exists( n ) ) then
			Framework.Data.New( n, v, DATA_PERSISTENT )
		end
	end
end )